﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using firstrpg;

using XRLib.INPUTHANDLER;
using System.Diagnostics;

namespace firstrpg
{

    [Flags]
    public enum camstatus
    {
        seeall = 0,            //can see all that is visible and normally lit.
        blind = 1,
        seeinvis = 2,            //5 invisible layers.
        seeinfra = 4,
        thermal = 8
    }


    public class Camera
    {

        
        public body Body;
        public body[] bodies_on_screen = new body[25];


        //position of the screen's top left vector coordinates
        public Vector2 screenPosition;
        public Rectangle screenBounds;
        public camstatus sight = camstatus.seeall;


        public Viewport View
        {
            get;
            private set;
        }
        public Vector2 Position
        {
            get;
            private set;
        }
        //center of focus for the camera
        public Vector2 FocusPoint
        {
            get;
            private set;
        }

        public float Zoom
        {
            get;
            private set;
        }
        public float Rotation
        {
            get;
            private set;
        }
        public Matrix Transform
        {
            get;
            private set;
        }

        public int left;

        TimeSpan shakeTimer;
        Random rand;


        /// <summary>
        /// Initialize a new Camera object
        /// </summary>
        /// <param name="view">The viewport we want the camera to use (holds dimensions and so on)</param>
        /// <param name="position">Where to point the center of the camera (0x0 will be the center of the viewport)</param>
        public Camera(Viewport view, Vector2 position)
        {
            View = view;
            Position = position;
            screenPosition = position;
            Zoom = 1.0f;
            Rotation = 0;
            rand = new Random();
            FocusPoint = new Vector2(view.Width / 2, view.Height / 2);
        }

        /// <summary>
        /// Initialize a new Camera object
        /// </summary>
        /// <param name="view">The viewport we want the camera to use (holds dimensions and so on)</param>
        /// <param name="position">Where to position our camera relative to the focus point</param>
        /// <param name="focus">Where to point the center of the camera (0x0 will be the center of the viewport)</param>
        /// <param name="zoom">How much we want the camera zoomed by default</param>
        /// <param name="rotation">How much we want the camera to be rotated by default</param>
        public Camera(Viewport view, Vector2 position, Vector2 focus, float zoom, float rotation)
        {
            View = view;
            Position = position;
            screenPosition = position;
            Zoom = zoom;
            Rotation = rotation;
            rand = new Random();
            FocusPoint = focus;
        }


        //what updates here is the entire way the camera perceives the world through certain eyes.
        //here is where it will get weird. we can either call this update method through the base
        //gamestate, so when its time to draw the gamescreen onto the gamescreen class, it will do so
        //but how to caluclate either to see invisible, thermal etc. we need a reference to a body or mind
        //
        public void Update(GameTime gameTime)
        {
            
            this.HandleInput(); //handle all inputs
            update_bodies_on_screen();


            left = (int)(FocusPoint.X - ((View.Width * 0.5f) / (Zoom * 10)) + 1f);


            if (client.mob != null)   //the client is controlling a body, set the source equal to it

                this.Body = client.mob;

            //set the screen position.
            

            setscreenPosition();    //set the screen position.
            screenBounds = new Rectangle((int)screenPosition.X, (int)screenPosition.Y, View.Width, View.Height);


            Vector2 objectPosition = Body != null ? Body.position : Position;
            this.Position = objectPosition;


            float objectRotation = Body != null ? Body.rotation : Rotation;
            float deltaRotation = 0.0f;



            Transform = Matrix.CreateTranslation(
                new Vector3(-objectPosition, 0)) *
                Matrix.CreateScale(new Vector3((float)Math.Pow(Zoom, 10), (float)Math.Pow(Zoom, 10), 1)) *
                Matrix.CreateRotationZ(-objectRotation + deltaRotation) *
                Matrix.CreateTranslation(new Vector3(FocusPoint.X, FocusPoint.Y, 0));

        }

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            //draws visible junk based on what the mind/body can see
            //this is where you can either 
            ///
            /// A. create an injection method in mind/body class that affects what the camera
            /// can see/can't see before its update/draw is called
            /// 
            /// B. read the mind/body container and find the client in this method, and
            /// call its method or just calculate junk accordingly.
            ///


            //grab the screen coords
            Rectangle range = new Rectangle(
                (int)screenPosition.X,
                (int)screenPosition.Y,
                (int)(screenPosition.X + (View.Width)),
                (int)(screenPosition.Y + (View.Height)));
            //lets draw the turfs in view
            turfs[] todraw = client.turfs.returnRange(range);
            int o = 0;

            foreach (turfs t in todraw)
            {

                spriteBatch.Draw(client.grasstexture, new Vector2(t.x * 32, t.y * 32), Color.White);
                o++;
            }




            foreach (item I in client.items)
            {
                if (I.loc is body)
                    continue;
                I.Draw(gameTime, spriteBatch);
            }
            //spritebatch is already open and in the transform mode.
            foreach (body B in client.bodies)
            {
                //here is where we will do invisibility checks to select certain bodies to draw or not.
                //or compare them in a list of things that are hidden or not.

                //what i would do is in the update, calculate what to draw, whats visible, blah blah in a list
                //then just draw that list here in the draw method.

                B.Draw(gameTime, spriteBatch);

            }
            

            //here we will draw all visible tiles within screen range.

        }

        /// <summary>
        /// cam.bodies_on_screen keeps record of all bodies that cross your screen bounds, called in cam.update()
        /// </summary>
        void update_bodies_on_screen()
        {
            int i = -1;
            foreach (body b in client.bodies)
            {
                i++;
                if (screenBounds.Contains((int)b.position.X, (int)b.position.Y))
                    bodies_on_screen[i] = b;
            }
        }


        void setscreenPosition()
        {

            /*
            screenPosition.X = (player[myPlayer].position.X + (player[myPlayer].width * 0.5f)) - (screenWidth * 0.5f);
            screenPosition.Y = (player[myPlayer].position.Y + (player[myPlayer].height * 0.5f)) - (screenHeight * 0.5f);
            screenPosition.X = (int)screenPosition.X;
            screenPosition.Y = (int)screenPosition.Y;
            */

            if (this.Body != null)
            {
                screenPosition.X = (this.Body.position.X + this.Body.width * 0.5f) - (client.screenWidth * 0.5f); //the left of the view of the player
                screenPosition.Y = (this.Body.position.Y + this.Body.height * 0.5f) - (client.screenHeight * 0.5f);
                screenPosition.X = (int)screenPosition.X;
                screenPosition.Y = (int)screenPosition.Y;
            }
            else    //they have no body.
            {
                screenPosition.X = (int)((this.FocusPoint.X) - this.View.Width);
                screenPosition.Y = (int)((this.FocusPoint.Y) - this.View.Height);
            }



            //LIMITS to the screen boundaries. please.


        }


        public void DrawString(string what, SpriteBatch batch)
        {

            batch.DrawString(client.defaultfont,
              what,
               new Vector2(0, 0),
               Color.White);
        }

        private void HandleInput()
        {
            if (InputHandler.KeyDown(Keys.Add))
                Zoom += 0.001f;
            if (InputHandler.KeyDown(Keys.Subtract))
                Zoom -= 0.001f;
            if (InputHandler.ScrollDown())
                Zoom -= 0.01f;
            if (InputHandler.ScrollUp())
                Zoom += 0.01f;




        }




    }
}
